/* eslint-disable @typescript-eslint/no-this-alias */
import { h } from "vue"
import { Graph, Addon, Shape, Platform } from '@antv/x6'
import "@antv/x6-vue-shape";
import { commonEdage } from '../const/config'
import { cloneDeep, set } from 'lodash'
import { designerStore } from 'src/stores/designerTool/designerTool'
import { layer_message, base64ToBlob, blobToFile, download } from 'src/utils/designerTool/toole';
import AlgoNode from 'src/pages/designerTool/canvas/DagEdit/components/AlgoNode/index'
import TextNode from 'src/pages/designerTool/canvas/DagEdit/components/TextNode/index'
import AlgoGroup from 'src/pages/designerTool/canvas/DagEdit/components/AlgoGroup/index'
// import { addWorkflow, updateWorkflow } from '@/api/group/workflow'
import { putWorkflows, postWorkflows } from 'src/api/designerTool'
import { instancesState, workflowSaveStateCode } from 'src/utils/designerTool/constants'
import { DagreLayout } from '@antv/layout'
import EncryptionDecryption from 'src/utils/designerTool/EncryptionDecryption'
import JSZip from 'jszip'
import "src/css/designerStyles/x6.scss";
const store = designerStore()
export default class DagGraph {
  static init(id, readonly = false) {
    if (this.isGraphReady()) {
      this.destroy()
    }
    const container = document.querySelector(id)
    this.graph = new Graph({
      container: container,
      autoResize: container,
      interacting: {
        nodeMovable: !readonly
      },
      // 网格
      grid: {
        size: 1,
        // 开启网格
        visible: false,
        args: {
          color: '#a0a0a0',
          thickness: 1
        }
      },
      // 画布调整
      scroller: {
        enabled: true,
        pageVisible: true,
        pageBreak: true,
        pannable: true,
        // modifiers: "ctrl",
        autoResize: true
      },
      // 小地图
      minimap: {
        width: 200,
        height: 126,
        enabled: true,
        scalable: false,
        container: document.getElementById('x6-minimap')
      },
      // 设置滚轮缩放画布
      mousewheel: {
        enabled: true,
        global: true,
        modifiers: 'ctrl',
        factor: 1.1
      },
      snapline: true, // 对齐线
      history: {
        enabled: !readonly,
        // ignoreAdd: false, // 是否忽略添加
        // ignoreRemove: false, // 是否忽略删除
        // ignoreChange: true,// 是否忽略属性变化
        beforeAddCommand(event, args) {
          return !(args.key === 'attrs' || args.key === 'data')
        }
      },
      clipboard: !readonly, // 剪切板用于复制/粘贴节点和边
      // 键盘快捷键
      keyboard: true,
      // 按ctrl框选节点
      selecting: {
        enabled: !readonly,
        multiple: true, // 是否启用点击多选
        rubberband: true, // 是否启用框选
        modifiers: 'ctrl', // 修饰键
        strict: true, // 严格包含
        movable: true, // 选中的节点是否一起移动
        showNodeSelectionBox: false,
        showEdgeSelectionBox: false
      },
      highlighting: {
        nodeAvailable: {
          name: 'className',
          args: {
            className: 'available'
          }
        },
        magnetAvailable: {
          name: 'className',
          args: {
            className: 'available'
          }
        },
        magnetAdsorbed: {
          name: 'className',
          args: {
            className: 'adsorbed'
          }
        },
        embedding: {
          name: 'className',
          args: {
            className: 'algo-group-move'
          }
        }
      },
      // 配置全局连线规则
      connecting: {
        anchor: 'center',
        connectionPoint: 'anchor',
        allowBlank: false,
        allowMulti: true,
        highlight: true,
        snap: {
          radius: 30
        },
        sourceAnchor: {
          name: 'bottom',
          args: {
            dx: Platform.IS_SAFARI ? 5 : 0
          }
        },
        targetAnchor: {
          name: 'center',
          args: {
            dx: Platform.IS_SAFARI ? 5 : 0
          }
        },
        createEdge() {
          const newEdge = new Shape.Edge(cloneDeep(commonEdage))
          return newEdge
        },
        validateMagnet({ magnet }) {
          return !readonly && magnet.getAttribute('port-group') !== 'in'
        },
        validateConnection({ sourceView,
          targetView,
          sourceMagnet,
          targetMagnet, sourcePort, targetPort, targetCell }) {
          if (readonly) {
            return false
          }
          if (sourceView === targetView) {
            return false
          }
          if (!sourceMagnet) {
            return false
          }
          // 只能连接到输入链接桩
          if (!targetMagnet || targetMagnet.getAttribute('port-group') !== 'inputs') {
            return false
          }
          // if (sourcePort !== targetPort && this.getIncomingEdges(targetCell)) {
          //   return !(this.getIncomingEdges(targetCell).some(row => row.getTargetPortId() === targetPort))
          // }
          return true
        },
        highlighting: {
          magnetAvailable: {
            name: 'stroke',
            args: {
              padding: 4,
              attrs: {
                strokeWidth: 4,
                stroke: '#006EFF'
              }
            }
          }
        }
      },
      // 节点拖拽到另一节点，形成父子节点关系(嵌套节点)
      embedding: {
        enabled: !readonly,
        validate({ parent }) {
          return parent.shape === 'vue-shape' && parent.component === 'algo-group'
        }
      },
      // 配置节点的可移动区域
      translating: {
        restrict(view) {
          if (view) {
            const cell = view.cell
            if (cell.isNode()) {
              const parent = cell.getParent()
              if (parent) {
                return cell.getBBox()
              }
            }
          }
          return null
        }
      },
      // 选框提示
      onPortRendered({ contentContainer, port, node }) {
        registerPortTooltip(contentContainer, port, node)
      }
    })

    const registerPortTooltip = (container, port, node) => {
      container.addEventListener('mouseenter', (e) => {
        const domRect = e.target.getBoundingClientRect()
        const p = this.graph.pageToLocal({ x: domRect.x, y: domRect.y })
        const p2 = this.graph.localToPage({ x: p.x + 10, y: p.y + 10 })
        store.SET_TOOLTIP_PORT({
          nodeId: node.data.id,
          portId: port.id,
          group: port.group,
          placement: port.group === 'inputs' ? 'top' : 'bottom',
          left: `${p2.x}px`,
          top: `${p2.y}px`,
          width: (domRect.width) + 'px',
          height: (domRect.height) + 'px'
        })
      })
      container.addEventListener('mouseleave', () => {
        store.SET_TOOLTIP_PORT({})
      })
    }

    // 注册边Connector，
    Graph.registerConnector(
      'curve',
      (s, t) => {
        const offset = 4
        const control = 80
        const v1 = { x: s.x, y: s.y + offset + control }
        const v2 = { x: t.x, y: t.y - offset - control }
        return `M ${s.x} ${s.y}
       L ${s.x} ${s.y + offset}
       C ${v1.x} ${v1.y} ${v2.x} ${v2.y} ${t.x} ${t.y - offset}
       L ${t.x} ${t.y}
      `
      },
      true
    )
    // 注册算子节点
    Graph.registerVueComponent('algo-node',
      {
        render() {
          return h(AlgoNode)
        },
      },
      true
    )

    // 注册文本框节点
    Graph.registerVueComponent('text-node',
      {
        render() {
          return h(TextNode)
        },
        // template: `<text-node></text-node>`,
        // components: {
        //   TextNode
        // }
      },
      true
    )
    // 算子组节点
    Graph.registerVueComponent('algo-group',
      {
        render() {
          return h(AlgoGroup)
        },
        // template: `<algo-group></algo-group>`,
        // components: {
        //   AlgoGroup
        // }
      },
      true
    )
    this.initEvent()
    return this.graph
  }
  //  初始化自定义模型树列表
  static initStencilTree(id) {
    const stencilWrap = document.querySelector(id)
    this.dnd = new Addon.Dnd({
      target: this.graph,
      scaled: true,
      containerParent: stencilWrap,
      animation: true
    })
    // stencilWrap.appendChild(this.dnd.container);
  }
  //  初始化自定义模型树列表
  static initBasicTree(id) {
    const stencilWrap = document.querySelector(id)
    this.basicDnd = new Addon.Dnd({
      target: this.graph,
      scaled: true,
      containerParent: stencilWrap
    })

    // stencilWrap.appendChild(this.dnd.container);
  }
  static initGraphShape(data) {
    // console.log(data)
    this.graph.fromJSON(data)
  }
  static initEvent() {
    const { graph } = this
    // 选中节点/边  #31a3ff
    // graph.on('cell:selected', ({ cell }) => {
    //
    // })
    // 取消选中节点/边时触发
    graph.on('cell:unselected', ({ cell }) => {
      store.SAVE_DAG(this.getDagData())
    })
    // 给相应的边添加标签；
    graph.on('edge:connected', ({ isNew, edge }) => {
      store.SAVE_DAG(this.getDagData())
      edge.attr({
        line: {
          strokeDasharray: ''
        }
      })
    })

    // 节点信息发生变化
    graph.on('node:change:data', ({ node }) => {
      const edges = graph.getIncomingEdges(node)
      const { status } = node.getData()
      let lineState = status.replace(/( |^)[a-z]/g, (L) => L.toUpperCase())
      edges?.forEach((edge) => {
        switch (lineState) {
          case instancesState.Success:
            edge.attr('line/strokeDasharray', 0)
            edge.attr('line/stroke', '#39CA74')
            edge.attr('line/style/animation', '')
            break
          case instancesState.Running:
            edge.attr('line/strokeDasharray', 5)
            edge.attr('line/stroke', '#B0BEC5')
            edge.attr('line/style/animation', 'running-line 30s infinite linear')
            break
          case instancesState.Failed:
            edge.attr('line/strokeDasharray', 0)
            edge.attr('line/stroke', '#ff4d4f')
            edge.attr('line/style/animation', '')
            break
          case instancesState.Paused:
            edge.attr('line/strokeDasharray', 0)
            edge.attr('line/stroke', '#E6A23C')
            edge.attr('line/style/animation', '')
            break
          default:
            edge.attr('line/strokeDasharray', '')
            edge.attr('line/stroke', '#B0BEC5')
            edge.attr('line/style/animation', '')
        }
      })
    })
  }
  // 销毁
  static destroy() {
    this.graph.dispose()
  }

  static isGraphReady() {
    return !!this.graph
  }

  // 缩放特定比例
  static zoomGraph(factor) {
    this.zoom(factor)
  }

  // 缩放到适应画布
  static zoomGraphToFit = () => {
    this.zoom('fit')
  }

  // 缩放到实际尺寸
  static zoomGraphRealSize = () => {
    this.zoom('real')
  }
  static zoom(factor) {
    if (typeof factor === 'number') {
      this.graph?.zoom(factor)
    } else if (factor === 'fit') {
      this.graph?.zoomToFit({ padding: 12 })
    } else if (factor === 'real') {
      this.graph?.scale(1)
      this.graph?.centerContent()
    }
  }

  // 复制节点
  static copyNode = () => {
    const cells = this.graph.getSelectedCells()
    if (cells.length) {
      for (let i = 0; i < cells.length; i++) {
        const data = cells[i].getData()
        data.status = ''
        cells[i].setData(data)
      }
      this.graph.copy(cells)
      store.SET_PASTE(true)
      layer_message('已复制至剪切板', 'success')
    }
  }

  // 粘贴节点
  static pasteNode = (x, y) => {
    if (!this.graph.isClipboardEmpty()) {
      const pasteConfig = {
        offset: 32,
        nodeProps: {}
      }
      if (x) {
        pasteConfig.nodeProps = {
          x, y
        }
      }
      const cells = this.graph.paste(pasteConfig)
      this.graph.resetSelection(cells)
    }
  }
  // 删除节点
  static removeCell = (cell) => {
    if (cell) {
      this.graph.removeCell(cell)
    }
  }
  // 删除边
  static removeEdge = (cell) => {
    if (cell) {
      this.graph.removeEdge(cell)
    }
  }
  static graphToPNG = () => {
    return new Promise((resolve, reject) => {
      this.graph.toPNG(
        (dataUri) => {
          const blob = base64ToBlob(dataUri)
          const file = blobToFile(blob, 'icon.png')
          resolve(file)
        },
        {
          backgroundColor: 'white',
          padding: {
            top: 20,
            right: 30,
            bottom: 40,
            left: 50
          },
          copyStyles: false,
          quality: 1,
          stylesheet: `
              .xflow-algo-node{
                z-index: 10;
                display: flex;
                align-items: center;
                width: 180px;
                height: 36px;
                text-align: center;
                background-color: #fff;
                border: 1px solid #d8d8d8;
                border-radius: 4px;
                box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
                transition: all 0.15s ease-in-out;
                font-size: 12px;
                font-family: Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Arial,sans-serif;
                position: absolute;
                top: 0;
                left: 30px;
              }
              .xflow-algo-node .icon {
                  flex: 0 0 36px;
                  width: 36px;
                  margin: -1px 1px -1px -1px;
                  border-radius: 4px 0 0 4px;
                  height: calc(100% + 2px);
                  display: flex;
                  justify-content: center;
                  align-items: center;
              }
              .xflow-algo-node .icon .svg-icon{
                  width: 1.2em;
                  height: 1.2em;
                  vertical-align: -0.3em;
                  fill: white;
              }
              .xflow-algo-node .center-container {
                width: 130px;
                height: 100%;
                display: flex;
                flex-direction: column;
                justify-content: center;
              }
              .xflow-algo-node .center-container .label {
                width: 100%;
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
                padding-left: 8px;
                text-align: left;
                display: inline-block;
              }
              `
        }
      )
    })
  }

  static funcLoading = () => {
    store.SET_WORKFLOW_SAVE_STATE(workflowSaveStateCode.loading)
  }
  static funcSuccess = (res) => {
    store.SET_WORKFLOW_INFO(res.data)
    store.SET_WORKFLOW_SAVE_TIME(res.data.update_time)
    store.SET_WORKFLOW_SAVE_STATE(workflowSaveStateCode.success)
  }
  static funcError = () => {
    store.SET_WORKFLOW_SAVE_TIME(new Date().getTime())
    store.SET_WORKFLOW_SAVE_STATE(workflowSaveStateCode.error)
  }

  // 保存修改工作流数据
  static saveOrUpdateWorkflowData = (saveVersion = false, versionDesc = '') => {
    this.funcLoading()
    return new Promise((resolve, reject) => {
      this.graphToPNG().then(file => {
        // 调用
        const workflowInfo = store.workflowInfo
        const form = new FormData()
        form.append('name', workflowInfo.name)
        form.append('description', workflowInfo.description)
        form.append('icon', file)
        form.append('dag', JSON.stringify(this.getDagData()))
        putWorkflows(store.project_id, store.workflow_id, form).then(res => {
          store.SET_WORKFLOW_CHANGE()
          if (saveVersion) {
            form.set('description', versionDesc)
            form.set('template_id', store.template_id)
            postWorkflows(store.project_id, form).then(res => {
              resolve(res)
              this.funcSuccess(res)
            }).catch(error => {
              reject(error)
            })
          } else {
            resolve(res)
            this.funcSuccess(res)
          }
        }, res => {
          reject(res)
          this.funcError()
        })
      }, res => {
        console.log(res)
      })
    })
  }

  // 导入工作流文件
  static saveImportWorkflowData = (icon, dag) => {
    this.funcLoading()
    return new Promise((resolve, reject) => {
      const workflowInfo = store.workflowInfo
      const form = new FormData()
      form.append('name', workflowInfo.name)
      form.append('description', workflowInfo.description)
      form.append('icon', icon)
      form.append('dag', JSON.stringify(dag))
      putWorkflows(store.project_id,workflowInfo.id, form).then(res => {
        store.SET_WORKFLOW_CHANGE()
        resolve(res)
        this.funcSuccess(res)
      }, res => {
        reject(res)
        this.funcError()
      })
    })
  }

  static getDagData = () => {
    const toJSON = this.graph.toJSON()
    // 还原初始值
    toJSON.cells.forEach(row => {
      if (row.shape === 'edge') {
        if (row.attrs && row.attrs.line) {
          row.attrs.line.strokeDasharray = ''
          row.attrs.line.stroke = '#B0BEC5'
          if (row.attrs.line.style) {
            row.attrs.line.style.animation = ''
          }
        }
      } else if (row.shape === 'vue-shape' && row.component === 'algo-node') {
        if (row.data.name) {
          row.data.configs.forEach(ele => {
            if (ele.chooseData != undefined) {
              delete ele.chooseData
            }
          })
        }
        row.data.status = ''
        delete row.ports.groups
      }
    })
    return toJSON
  }

  static automaticLayout = () => {
    const data = {
      nodes: [],
      edges: []
    }
    const toJSON = this.graph.toJSON().cells
    for (let j = 0; j < toJSON.length; j++) {
      if (toJSON[j].shape === 'edge') {
        data.edges.push(toJSON[j])
      } else {
        data.nodes.push(toJSON[j])
      }
    }
    const dagreLayout = new DagreLayout({
      type: 'dagre',
      ranksep: 30,
      nodesep: 15,
      controlPoints: true
    })
    const model = dagreLayout.layout(data)

    this.initGraphShape(model)

    this.zoomGraphRealSize()
  }

  static exportWorkflow = () => {
    const that = this
    this.graphToPNG().then(file => {
      const zip = new JSZip()
      zip.file('image.png', file)
      const { name, description } = store.workflowInfo
      const title = name + new Date().getTime()
      const jsonData = {
        workflow: {
          name,
          description
        },
        dag: that.getDagData()
      }
      zip.file(title + '.workflow', EncryptionDecryption.Encrypt(jsonData))
      zip.generateAsync({
        type: 'blob'
      }).then(content => { // 生成二进制流
        download(URL.createObjectURL(content), title + '.zip')
      })
    }, res => {
      console.log()
    }).finally(() => {
      console.log()
    })
  }
}
